package org.luxor.commons.zookeeper.lock;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.luxor.commons.core.exception.GlobalLockException;
import org.luxor.commons.core.utils.AssertUtils;
import org.luxor.commons.zookeeper.annotation.GlobalLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * 分布式锁AOP切面
 *
 * @author zengzh
 * @since 1.0.0
 */
public class ZkLockInterceptor implements MethodInterceptor {
    private static final Logger log = LoggerFactory.getLogger(ZkLockInterceptor.class);
    private ZkLockKeyGenerator keyGenerator = new ZkLockKeyGenerator();
    private CuratorFramework curatorFramework;

    public ZkLockInterceptor(CuratorFramework curatorFramework) {
        this.curatorFramework = curatorFramework;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        InterProcessMutex mutex = null;
        GlobalLock globalLock = invocation.getMethod().getAnnotation(GlobalLock.class);
        String path = null;
        try {
            path = keyGenerator.getKeyName(invocation, globalLock);
            mutex = new InterProcessMutex(curatorFramework, path);
            // 获取可重入排他锁
            if (globalLock.timeout() == -1) {
                mutex.acquire();
            } else {
                AssertUtils.isTrue(mutex.acquire(globalLock.timeout(), TimeUnit.NANOSECONDS), "获取锁超时");
            }
            log.trace(">>>  获取分布式锁(GlobalLock)完成. path:{}", path);
            return invocation.proceed();
        } catch (Exception e) {
            log.error("获取分布式锁(GlobalLock)失败.", e);
            throw new GlobalLockException(globalLock.errorCode(), globalLock.errorMsg());
        } finally {
            try {
                mutex.release();
                log.trace("<<< 释放分布式锁(GlobalLock)完成. path:{}", path);
            } catch (Exception e) {
                log.error("释放分布式锁(GlobalLock)失败.", e);
            }
        }
    }

}
